/* 
 -----------------------------------------------------------------------------
                   Cogaen - Component-based Game Engine (v3)
 -----------------------------------------------------------------------------
 This software is developed by the Cogaen Development Team. Please have a 
 look at our project home page for further details: http://www.cogaen.org
    
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Copyright (c) Roman Divotkey, 2010-2011

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/

package org.cogaen.time;

/**
 * Used to measure elapsed time using Java's high precision timer.
 * 
 * The delta time is limited to a maximum value of 250 milliseconds in order
 * to facilitate debugging.
 */
public class Clock {
	
	/** Maximum delta time in nanoseconds. */
	private static final long MAX_DELTATIME = 250000000;
	
	/** Represents the duration of one nanosecond in seconds. */
	private static final double ONE_NANOSECOND = 0.000000001;
	
	/** Represents the duration of one second in nanoseconds. */
	public static final long ONE_SECOND = 1000000000;
	
	/** Number of ticks used to calculate average delta time. */
	private static final int MAX_CNT_TICKS = 15;
	
	/** Current time. */
	private long time;
	
	/** Current delta time. */
	private long dt;
	
	/** Last time time stamp. */
	private long last;	
	
	/** Counter used to calculate average updates per second. */
	private int cntTicks;
	
	/** Last time stamp used to calculate average updates per second. */
	private long lastAvg;
	
	/** Average delta time. */
	private long dtAvg;
	
	/**
	 * Creates a new instance of <code>Clock</clock>.
	 * <Code>reset</code> is called during initialization.
	 */
	public Clock() {
		reset();
	}
	
	/**
	 * Resets all values to zero. 
	 */
	public void reset() {
		this.time = 0;
		this.dt = 0;
		this.last = System.nanoTime();
		
		this.dtAvg = 0;
		this.lastAvg = this.last;		
		this.cntTicks = MAX_CNT_TICKS;
	}
	
	/**
	 * Calculates new values for time and delta time by measuring the current
	 * time using Java's high precision timer. 
	 */
	public void tick() {
		// calculate new delta time and absolute time
		long now = System.nanoTime();
		dt = (now - last);
		last = now;
		if (dt > MAX_DELTATIME) {
			dt = MAX_DELTATIME;
		}
		time += dt;
		
		// update average delta time used to calculate average updates per second
		this.cntTicks--;
		if (this.cntTicks <= 0) {
			this.dtAvg = (now - this.lastAvg) / MAX_CNT_TICKS;
			this.lastAvg = now;
			this.cntTicks = MAX_CNT_TICKS;
		}
	}
	
	/**
	 * Returns the current delta time in seconds.
	 * 
	 * @return current delta time in seconds.
	 */
	public double getDelta() {
		return dt * ONE_NANOSECOND;
	}
		
	/** 
	 * Returns the current absolute time in seconds.
	 *
	 * @return current absolute time in seconds.
	 */
	public double getTime() {
		return time * ONE_NANOSECOND;
	}
	
	/**
	 * Returns the average updates in seconds (UPS).
	 * 
	 * @return average updates in seconds.
	 */
	public double getUps() {
		return ONE_SECOND / (double) this.dtAvg;
	}
	
	public double getAvgDelta() {
		return this.dtAvg * ONE_NANOSECOND;
	}
	
	public long getNanoDelta() {
		return this.dt;
	}
	
	/**
	 * Indicates if an new value for average updates in seconds is available.
	 * 
	 * @return <code>true</code> if a new value for average updates in
	 * seconds is available, <code>false</code> otherwise.
	 */
	public boolean isNewUps() {
		return this.cntTicks == MAX_CNT_TICKS;		
	}
}